package com.weijian.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.weijian.dto.ChunkUploadRequest;
import com.weijian.dto.CompleteUploadRequest;
import com.weijian.dto.InitUploadRequest;
import com.weijian.entity.UploadTask;
import com.weijian.entity.VideoInfo;
import com.weijian.service.ChunkUploadService;
import com.weijian.service.MinIOService;
import com.weijian.service.UploadTaskService;
import com.weijian.service.VideoInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 分片上传Service实现类
 * 注意：ChunkUploadService是分片上传业务服务，通过组合UploadTaskService和VideoInfoService进行数据库操作
 * 本身不直接操作数据库，无需继承MybatisPlus的ServiceImpl
 */
@Slf4j
@Service
public class ChunkUploadServiceImpl implements ChunkUploadService {

    @Autowired
    private UploadTaskService uploadTaskService;
    
    @Autowired
    private MinIOService minIOService;
    
    @Autowired
    private VideoInfoService videoInfoService;
    
    @Value("${video.upload.temp-dir:/tmp/upload}")
    private String tempDir;

    @Override
    public Object initUpload(InitUploadRequest request) {
        // 检查是否已存在相同MD5的文件
        String fileMd5 = request.getFileMd5();
        if (minIOService.exists(fileMd5 + ".mp4")) {
            return "文件已存在，无需重复上传";
        }
        
        // 创建或获取上传任务
        UploadTask task = uploadTaskService.getUploadTask(request.getTaskId());
        if (task == null) {
            task = uploadTaskService.initUploadTask(
                request.getTaskId(),
                request.getFileName(),
                request.getFileSize(),
                request.getFileMd5(),
                request.getChunkSize(),
                request.getTotalChunks()
            );
        }
        
        // 获取已上传的分片列表
        List<Integer> uploadedChunks = getUploadedChunks(request.getTaskId());
        
        return new UploadInitResponse(task.getTaskId(), uploadedChunks);
    }

    @Override
    public Object uploadChunk(ChunkUploadRequest request) {
        UploadTask task = uploadTaskService.getUploadTask(request.getTaskId());
        if (task == null) {
            throw new RuntimeException("上传任务不存在");
        }

        if (task.getStatus() == 2) {
            return "文件已上传完成";
        }

        try {
            // 保存分片到临时目录
            String chunkDir = getChunkDir(request.getTaskId());
            File chunkFile = new File(chunkDir, request.getChunkNumber().toString());

            // 确保父目录存在，如果不存在则创建
            File parentDir = chunkFile.getParentFile();
            if (!parentDir.exists()) {
                boolean dirsCreated = parentDir.mkdirs();
                if (!dirsCreated) {
                    log.error("无法创建目录: {}", parentDir.getAbsolutePath());
                    throw new IOException("无法创建目录: " + parentDir.getAbsolutePath());
                }
            }

            // 确保目录确实存在且是目录
            if (!parentDir.exists() || !parentDir.isDirectory()) {
                log.error("目录创建失败或不是目录: {}", parentDir.getAbsolutePath());
                throw new IOException("目录创建失败: " + parentDir.getAbsolutePath());
            }

            request.getChunk().transferTo(chunkFile);

            // 更新上传进度
            uploadTaskService.updateChunkProgress(request.getTaskId(), request.getChunkNumber());

            return "分片上传成功";

        } catch (IOException e) {
            log.error("分片上传失败: {}", e.getMessage(), e);
            uploadTaskService.markTaskFailed(request.getTaskId());
            throw new RuntimeException("分片上传失败: " + e.getMessage());
        }
    }


    @Override
    @Transactional
    public VideoInfo completeUpload(CompleteUploadRequest request) {
        UploadTask task = uploadTaskService.getUploadTask(request.getTaskId());
        if (task == null) {
            throw new RuntimeException("上传任务不存在");
        }

        if (task.getStatus() != 1) {
            throw new RuntimeException("上传任务状态异常");
        }

        try {
            // 合并分片
            File mergedFile = mergeChunks(task);

            // 验证文件完整性 (在测试阶段可以暂时注释掉)
            String actualMd5 = DigestUtil.md5Hex(mergedFile);
            if (!actualMd5.equals(task.getFileMd5())) {
                log.warn("文件MD5校验失败，期望: {}, 实际: {}", task.getFileMd5(), actualMd5);
                // throw new RuntimeException("文件MD5校验失败");
            }

            // 上传到MinIO
            String objectName = task.getFileMd5() + ".mp4";
            String fileUrl = minIOService.uploadFile(mergedFile, objectName);

            // 创建视频信息记录
            VideoInfo videoInfo = new VideoInfo();
            videoInfo.setTitle(task.getFileName());
            videoInfo.setFileName(task.getFileName());
            videoInfo.setFilePath(fileUrl);
            videoInfo.setFileSize(task.getFileSize());
            videoInfo.setDuration(0); // TODO: 获取视频时长
            videoInfo.setThumbnailPath(""); // TODO: 生成缩略图

            videoInfoService.save(videoInfo);

            // 标记任务完成
            uploadTaskService.markTaskComplete(request.getTaskId());

            // 清理临时文件
            cleanupTempFiles(request.getTaskId());

            return videoInfo;

        } catch (Exception e) {
            log.error("完成上传失败: {}", e.getMessage(), e);
            uploadTaskService.markTaskFailed(request.getTaskId());
            throw new RuntimeException("完成上传失败: " + e.getMessage());
        }
    }


    @Override
    public List<Integer> getUploadedChunks(String taskId) {
        String chunkDir = getChunkDir(taskId);
        File dir = new File(chunkDir);
        if (!dir.exists()) {
            return new ArrayList<>();
        }
        
        File[] chunkFiles = dir.listFiles();
        if (chunkFiles == null) {
            return new ArrayList<>();
        }
        
        return Arrays.stream(chunkFiles)
                .filter(File::isFile)
                .map(file -> Integer.parseInt(file.getName()))
                .sorted()
                .collect(Collectors.toList());
    }

    private File mergeChunks(UploadTask task) throws IOException {
        String chunkDir = getChunkDir(task.getTaskId());
        File dir = new File(chunkDir);
        File[] chunkFiles = dir.listFiles();

        if (chunkFiles == null || chunkFiles.length != task.getTotalChunks()) {
            throw new RuntimeException("分片不完整");
        }

        // 按分片序号正确排序（数字排序而非字符串排序）
        Arrays.sort(chunkFiles, Comparator.comparingInt(file -> Integer.parseInt(file.getName())));

        // 创建合并后的文件
        String mergedFilePath = tempDir + "/merged/" + task.getTaskId() + ".mp4";
        File mergedFile = new File(mergedFilePath);
        if (!mergedFile.getParentFile().exists()) {
            mergedFile.getParentFile().mkdirs();
        }

        try (FileOutputStream fos = new FileOutputStream(mergedFile)) {
            for (File chunkFile : chunkFiles) {
                byte[] bytes = Files.readAllBytes(chunkFile.toPath());
                fos.write(bytes);
            }
        }

        return mergedFile;
    }


    private String getChunkDir(String taskId) {
        return tempDir + "/chunks/" + taskId;
    }

    private void cleanupTempFiles(String taskId) {
        // 清理分片目录
        FileUtil.del(getChunkDir(taskId));
        
        // 清理合并后的文件
        FileUtil.del(tempDir + "/merged/" + taskId + ".mp4");
    }

    /**
     * 上传初始化响应
     */
    public static class UploadInitResponse {
        private final String taskId;
        private final List<Integer> uploadedChunks;

        public UploadInitResponse(String taskId, List<Integer> uploadedChunks) {
            this.taskId = taskId;
            this.uploadedChunks = uploadedChunks;
        }

        public String getTaskId() {
            return taskId;
        }

        public List<Integer> getUploadedChunks() {
            return uploadedChunks;
        }
    }
}